﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisualStock.Infrastructure;
using VisualStock.Infrastructure.Interfaces;
using VisualStock.Infrastructure.Models;
using VisualStock.Modules.StockMarket.Services;
using VisualStock.Modules.StockMarket.Tests.Mocks;

namespace VisualStock.Modules.StockMarket.Tests.Services
{
    [TestClass]
    public class DataAnalysisServiceTest
    {
        DateTime[] testDates =
            new DateTime[10] { 
                new DateTime(2010, 1, 1), // Friday
                new DateTime(2010, 1, 4), // Monday
                new DateTime(2010, 1, 5), 
                new DateTime(2010, 1, 6), 
                new DateTime(2010, 1, 7),
                new DateTime(2010, 1, 8), // Friday
                new DateTime(2010, 1, 11),
                new DateTime(2010, 1, 12),
                new DateTime(2010, 1, 13),
                new DateTime(2010, 1, 14)
            };

        TestableDataAnalysisService dataAnalysis;

        [TestInitialize]
        public void Initialize()
        {
            var eventAggregator = new MockCacheAnalysisDataEventAggregator();
            dataAnalysis = new TestableDataAnalysisService(null, eventAggregator);
        }

        [TestMethod]
        public void RelativeXValueVerification()
        {
            Dictionary<DateTime, int> expectedTable = new Dictionary<DateTime, int>();
            expectedTable.Add(testDates[0], 0);
            expectedTable.Add(testDates[1], 1);
            expectedTable.Add(testDates[2], 2);
            expectedTable.Add(testDates[3], 3);
            expectedTable.Add(testDates[4], 4);

            Dictionary<DateTime, int> actualTable = DataAnalysisService.RelativeXValueCalculation(testDates[0], testDates[4]);
            for (int i = 0; i < 5; i++)
            {
                Assert.AreEqual(expectedTable[testDates[i]], actualTable[testDates[i]]);
            }
        }

        [TestMethod]
        public void QuickMovingAverageCalculationVerfication()
        {
            StockHistoricalDataCollection testDataSource = new StockHistoricalDataCollection();
            // In Newest first order
            for (int i = 9; i >= 0; i--)
            {
                testDataSource.Add(new StockHistoricalDataItem
                {
                    QuoteDate = testDates[i],
                    ClosingPrice = i + 1
                });
            }

            StockHistoricalDataCollection expectedResult = new StockHistoricalDataCollection();
            for (int j = 9; j >= 5; j--)
            {
                expectedResult.Add(new StockHistoricalDataItem
                {
                    QuoteDate = testDates[j],
                    ClosingPrice = j - 2
                });
            }

            StockHistoricalDataCollection actualResult = dataAnalysis.QuickMovingAverageCalculation(testDataSource, 5);
            Assert.AreEqual(expectedResult.StartDate, actualResult.StartDate);
            Assert.AreEqual(expectedResult.EndDate, actualResult.EndDate);
            for (int k = 0; k < 5; k++)
            {
                Assert.AreEqual(expectedResult[k].ClosingPrice, actualResult[k].ClosingPrice);
            }
        }

        [TestMethod]
        public void TestDefaultMACwithMissingData()
        {
            StockHistoricalDataCollection testDataSource = new StockHistoricalDataCollection();
            // In Newest first order
            for (int i = 9; i >= 0; i--)
            {
                if (i == 5) continue; // Skip one date.
                testDataSource.Add(new StockHistoricalDataItem
                {
                    QuoteDate = testDates[i],
                    ClosingPrice = i + 1
                });
            }
            StockHistoricalDataCollection expectedResult = new StockHistoricalDataCollection()
                {
                    new StockHistoricalDataItem{QuoteDate=testDates[9],ClosingPrice=7.25},
                    new StockHistoricalDataItem{QuoteDate=testDates[8],ClosingPrice=6},
                    new StockHistoricalDataItem{QuoteDate=testDates[7],ClosingPrice=4.75},
                    new StockHistoricalDataItem{QuoteDate=testDates[6],ClosingPrice=3.5}
                };
            StockHistoricalDataCollection actualResult = dataAnalysis.DefaultMovingAverageCalculation(testDataSource, 5);
            // One data is missing, therefore only 4 
            for (int k = 0; k < 4; k++)
            {
                Assert.AreEqual(expectedResult[k].ClosingPrice, actualResult[k].ClosingPrice);
            }
        }

        [TestMethod]
        public void DefaultMACAndQuickMACHaveSameResult()
        {
            const int TEST_MA_DAYS = 100;
            const int TEST_DATA_AMOUNT = 1000;

            StockHistoricalDataCollection testDataSource = new StockHistoricalDataCollection();
            DateTime currentDate = new DateTime(2010, 6, 30);
            Random rand = new Random(DateTime.Now.Second);

            // Generate a random mock HistoricalDataCollection which has 50 items
            for (int i = 0; i < TEST_DATA_AMOUNT; i++)
            {
                testDataSource.Add(new StockHistoricalDataItem
                {
                    QuoteDate = currentDate,
                    ClosingPrice = rand.NextDouble() * 100.0
                });
                currentDate = currentDate.AddDays(-1);
                if (currentDate.DayOfWeek == DayOfWeek.Sunday) currentDate = currentDate.AddDays(-2);// Skip Sunday and Saturday
            }

            StockHistoricalDataCollection quickResult = this.dataAnalysis.QuickMovingAverageCalculation(testDataSource, TEST_MA_DAYS);
            StockHistoricalDataCollection defaultResult = this.dataAnalysis.DefaultMovingAverageCalculation(testDataSource, TEST_MA_DAYS);

            for (int k = 0; k < TEST_DATA_AMOUNT - TEST_MA_DAYS; k++)
            {
                Assert.AreEqual(quickResult[k].ClosingPrice, defaultResult[k].ClosingPrice, 0.03);
            }
        }

        private class TestableDataAnalysisService : DataAnalysisService
        {
            public TestableDataAnalysisService(IHistoricalDataService historicalDataService, MockCacheAnalysisDataEventAggregator eventAggregator)
                : base(historicalDataService, eventAggregator)
            {
            }
        }

        private class MockCacheAnalysisDataEventAggregator : MockEventAggregator
        {
            public MockCacheAnalysisDataEvent _mockCacheAnalysisDataEvent = new MockCacheAnalysisDataEvent();
            public MockCacheAnalysisDataEventAggregator()
            {
                AddMapping<CacheAnalysisDataEvent>(_mockCacheAnalysisDataEvent);
            }

            public class MockCacheAnalysisDataEvent : CacheAnalysisDataEvent
            {
                public bool PublishCalled;
                public KeyValuePair<string, StockHistoricalDataCollection> PublishArgumentPayload;
                public EventHandler PublishCalledEvent;

                private void OnPublishCalledEvent(object sender, EventArgs args)
                {
                    if (PublishCalledEvent != null)
                        PublishCalledEvent(sender, args);
                }

                public override void Publish(KeyValuePair<string, StockHistoricalDataCollection> payload)
                {
                    PublishCalled = true;
                    PublishArgumentPayload = payload;
                    OnPublishCalledEvent(this, EventArgs.Empty);
                }
            }
        }
    }
}
